package cn.iocoder.yudao.module.member.statistics.service;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.business.basic.convert.BigDecimalConvert;
import cn.iocoder.yudao.framework.business.basic.pojo.bo.DataComparison;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.region.RegionUtils;
import cn.iocoder.yudao.framework.region.bo.RegionNode;
import cn.iocoder.yudao.framework.region.enums.RegionDataSet;
import cn.iocoder.yudao.framework.region.enums.RegionType;
import cn.iocoder.yudao.module.api.brokerage.user.BrokerageUserApi;
import cn.iocoder.yudao.module.api.pay.statistics.PayStatisticsApi;
import cn.iocoder.yudao.module.api.pay.statistics.dto.PayWalletRechargeSummaryRespDTO;
import cn.iocoder.yudao.module.api.pay.wallet.PayWalletApi;
import cn.iocoder.yudao.module.api.pay.wallet.dto.PayWalletRespDTO;
import cn.iocoder.yudao.module.api.promotion.coupon.CouponRecordApi;
import cn.iocoder.yudao.module.api.promotion.coupon.enums.CouponStatusEnum;
import cn.iocoder.yudao.module.api.promotion.gift.GiftRecordApi;
import cn.iocoder.yudao.module.api.promotion.gift.enums.GiftRecordStatus;
import cn.iocoder.yudao.module.api.promotion.redpacket.RedPacketApi;
import cn.iocoder.yudao.module.api.trade.statistics.TradeStatisticsApi;
import cn.iocoder.yudao.module.api.trade.statistics.dto.TradeOrderAreaStatisticsRespDTO;
import cn.iocoder.yudao.module.member.assets.dal.dataobject.MemberDigitalAssetsDO;
import cn.iocoder.yudao.module.member.assets.service.AppMemberDigitalAssetsService;
import cn.iocoder.yudao.module.member.statistics.convert.MemberStatisticsConvert;
import cn.iocoder.yudao.module.member.statistics.dao.MemberStatisticsMapper;
import cn.iocoder.yudao.module.member.statistics.pojo.bo.MemberAreaStatisticsRespBO;
import cn.iocoder.yudao.module.member.statistics.pojo.bo.MemberRegisterCountRespBO;
import cn.iocoder.yudao.module.member.statistics.pojo.bo.MemberSexStatisticsRespBO;
import cn.iocoder.yudao.module.member.statistics.pojo.bo.MemberTerminalStatisticsRespBO;
import cn.iocoder.yudao.module.member.statistics.pojo.vo.*;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;

/**
 * 用户信息的统计 Service 实现类
 *
 * @author owen
 */
@Service
@Validated
public class MemberStatisticsServiceImpl implements MemberStatisticsService {

    @Resource
    private MemberStatisticsMapper memberStatisticsMapper;

    @Resource
    private AppMemberDigitalAssetsService memberDigitalAssetsService;

    @Resource
    private BrokerageUserApi brokerageUserApi;
    @Resource
    private PayStatisticsApi payStatisticsApi;
    @Resource
    private PayWalletApi payWalletApi;
    @Resource
    private TradeStatisticsApi tradeStatisticsApi;
    @Resource
    private CouponRecordApi couponRecordApi;
    @Resource
    private GiftRecordApi giftRecordApi;
    @Resource
    private RedPacketApi redPacketApi;

    @Override
    public MemberSummaryRespVO getMemberSummary() {
        PayWalletRechargeSummaryRespDTO rechargeSummary = payStatisticsApi.getWalletRechargeSummary(null, null);
        // TODO @疯狂：1）这里是实时统计，不好走走 TradeStatistics 表；2）因为这个放在商城下，所以只考虑订单数据，即按照 trade_order 的 pay_price 并且已支付来计算；
        Integer expensePrice = tradeStatisticsApi.getExpensePrice(null, null);
        Integer userCount = memberStatisticsMapper.selectUserCount(null, null);
        return MemberStatisticsConvert.INSTANCE.convert(rechargeSummary, expensePrice, userCount);
    }

    @Override
    public List<MemberAreaStatisticsRespVO> getMemberAreaStatisticsList() {
        List<RegionNode> provinceList = RegionUtils.getRegionListByType(RegionDataSet.ONLY_LATEST, RegionType.PROVINCE, regionNode -> regionNode);
        provinceList.add(new RegionNode().setCode(null).setName("未知").setShortName("未知").setAlphabet("#"));
        // 统计每个区域的用户数
        Map<Integer, Integer> userCountMap = convertMap(memberStatisticsMapper.selectSummaryListGroupByAreaId(),
                MemberAreaStatisticsRespBO::getAreaId,
                MemberAreaStatisticsRespBO::getUserCount, Integer::sum);
        // 统计每个收货省份的订单数
        Map<Integer, TradeOrderAreaStatisticsRespDTO> orderCountMap = convertMap(tradeStatisticsApi.getSummaryListGroupByAreaId(),
                TradeOrderAreaStatisticsRespDTO::getAreaId,
                bo -> bo,
                (a, b) -> new TradeOrderAreaStatisticsRespDTO()
                        .setOrderCreateUserCount(ObjectUtil.defaultIfNull(a.getOrderCreateUserCount(), 0)
                                                 + ObjectUtil.defaultIfNull(b.getOrderCreateUserCount(), 0))
                        .setOrderPayUserCount(ObjectUtil.defaultIfNull(a.getOrderPayUserCount(), 0)
                                              + ObjectUtil.defaultIfNull(b.getOrderPayUserCount(), 0))
                        .setOrderPayPrice(ObjectUtil.defaultIfNull(a.getOrderPayPrice(), 0)
                                          + ObjectUtil.defaultIfNull(b.getOrderPayPrice(), 0)));
        // 拼接数据
        return MemberStatisticsConvert.INSTANCE.convertList(provinceList, userCountMap, orderCountMap);
    }

    @Override
    public DataComparison<MemberAnalyseDataRespVO> getMemberAnalyseComparisonData(LocalDateTime beginTime, LocalDateTime endTime) {
        // 当前数据
        MemberAnalyseDataRespVO vo = getMemberAnalyseData(beginTime, endTime);
        // 对照数据
        LocalDateTime referenceEndDate = beginTime.minusDays(1); // 减少1天，防止出现时间重叠
        LocalDateTime referenceBeginDate = referenceEndDate.minus(Duration.between(beginTime, endTime));
        MemberAnalyseDataRespVO reference = getMemberAnalyseData(
                LocalDateTimeUtil.beginOfDay(referenceBeginDate), LocalDateTimeUtil.endOfDay(referenceEndDate));
        return new DataComparison<>(vo, reference);
    }

    private MemberAnalyseDataRespVO getMemberAnalyseData(LocalDateTime beginTime, LocalDateTime endTime) {
        Integer rechargeUserCount = Optional.ofNullable(payStatisticsApi.getWalletRechargeSummary(beginTime, endTime))
                .map(PayWalletRechargeSummaryRespDTO::getRechargeUserCount).orElse(0);
        MemberAnalyseDataRespVO respVO = new MemberAnalyseDataRespVO();
        respVO.setRegisterUserCount(memberStatisticsMapper.selectUserCount(beginTime, endTime));
        respVO.setVisitUserCount(getUserCount(UserTypeEnum.MEMBER.getValue(), beginTime, endTime));
        respVO.setRechargeUserCount(rechargeUserCount);
        return respVO;
    }

    @Override
    public List<MemberSexStatisticsRespBO> getMemberSexStatisticsList() {
        return memberStatisticsMapper.selectSummaryListBySex();
    }

    @Override
    public List<MemberTerminalStatisticsRespBO> getMemberTerminalStatisticsList() {
        return memberStatisticsMapper.selectSummaryListByRegisterTerminal();
    }

    @Override
    public List<MemberRegisterCountRespBO> getMemberRegisterCountList(LocalDateTime beginTime, LocalDateTime endTime) {
        return memberStatisticsMapper.selectListByCreateTimeBetween(beginTime, endTime);
    }

    @Override
    public DataComparison<MemberCountRespVO> getUserCountComparison() {
        // 今日时间范围
        LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
        LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(beginOfToday);
        // 昨日时间范围
        LocalDateTime beginOfYesterday = LocalDateTimeUtil.beginOfDay(beginOfToday.minusDays(1));
        LocalDateTime endOfYesterday = LocalDateTimeUtil.endOfDay(beginOfYesterday);
        return new DataComparison<MemberCountRespVO>()
                .setValue(getUserCount(beginOfToday, endOfToday))
                .setReference(getUserCount(beginOfYesterday, endOfYesterday));
    }

    private MemberCountRespVO getUserCount(LocalDateTime beginTime, LocalDateTime endTime) {
        MemberCountRespVO respVO = new MemberCountRespVO();
        respVO.setRegisterUserCount(memberStatisticsMapper.selectUserCount(beginTime, endTime));
        respVO.setVisitUserCount(getIpCount(UserTypeEnum.MEMBER.getValue(), beginTime, endTime));
        return respVO;
    }

    @Override
    public Integer getUserCount(Integer userType, LocalDateTime beginTime, LocalDateTime endTime) {
        return memberStatisticsMapper.selectUserCountByUserTypeAndCreateTimeBetween(userType, beginTime, endTime);
    }

    @Override
    public Integer getIpCount(Integer userType, LocalDateTime beginTime, LocalDateTime endTime) {
        return memberStatisticsMapper.selectIpCountByUserTypeAndCreateTimeBetween(userType, beginTime, endTime);
    }

    @Override
    public AppMemberCenterSummaryRespVO getMemberCenterSummaryAndSyncToRedis(Long userId) {
        if (userId == null || userId <= 0) {
            return new AppMemberCenterSummaryRespVO();
        }
        AppMemberCenterSummaryRespVO respVO = new AppMemberCenterSummaryRespVO();
        PayWalletRespDTO wallet = payWalletApi.getOrCreateWallet(UserTypeEnum.MEMBER.getValue(), userId);
        MemberDigitalAssetsDO digitalAssets = memberDigitalAssetsService.getOrCreate(userId);
        respVO.setCouponCount(couponRecordApi.getCouponCountByUserId(userId, null));
        respVO.setCouponUnusedCount(couponRecordApi.getCouponCountByUserId(userId, CouponStatusEnum.UNUSED.getStatus()));
        respVO.setGiftCount(giftRecordApi.getCountByUserId(userId, null));
        respVO.setGiftUnusedCount(giftRecordApi.getCountByUserId(userId, GiftRecordStatus.PENDING.getValue()));
        respVO.setShopRedPacketAmount(redPacketApi.getRedPacketAmount(userId, null));
        respVO.setPlatformRedPacketAmount(BigDecimalConvert.INSTANCE.convert(digitalAssets.getRedPacket()).intValue());
        respVO.setInviteDirectCount(brokerageUserApi.getFatherCount(userId));
        respVO.setInviteIndirectCount(brokerageUserApi.getGrandpaCount(userId));
        respVO.setTotalPoint(digitalAssets.getPoint());
        respVO.setTotalCryptocurrency(digitalAssets.getCryptocurrency());
        respVO.setTotalBalance(wallet.getBalance());
        respVO.setTotalMoney(wallet.getMoney());
        respVO.setTotalMoneyCashing(wallet.getMoneyCashing());
        return respVO;
    }

}
